package fons.navigator.util.settings;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

import com.google.common.collect.Lists;

@SuppressWarnings({"unchecked", "rawtypes"})
public class Configuration {
	private List<Class<?>> settingTypes = Lists.newArrayList();
	private List settingValues = Lists.newArrayList();
	private List<String> settingNames = Lists.newArrayList();
	private List<String> settingDescriptions = Lists.newArrayList();
	private List<Boolean> settingMandatory = Lists.newArrayList();
	
	private List<Configuration> stack = Lists.newLinkedList();
	
	public void push () {
		stack.add(copy());
	}
	
	public void pop () {
		if (stack.size() == 0) throw new IllegalStateException();
		
		Configuration top = stack.remove(stack.size()-1);
		this.settingTypes = top.settingTypes;
		this.settingValues = top.settingValues;
		this.settingNames = top.settingNames;
		this.settingDescriptions = top.settingDescriptions;
		this.settingMandatory = top.settingMandatory;
	}
	
	
	public Configuration copy () {
		Configuration copy = new Configuration();
		for (int i = 0; i < getSize(); ++i) {
			Object o = settingValues.get(i);
			if (o != null && o instanceof Configuration) {
				o = ((Configuration) o).copy();
			}
			copy.settingValues.add(o);
		}
		copy.settingTypes = Lists.newArrayList(settingTypes);
			
			copy.settingNames = Lists.newArrayList(settingNames);
			copy.settingDescriptions = Lists.newArrayList(settingDescriptions);
			copy.settingMandatory = Lists.newArrayList(settingMandatory);
		
		return copy;
	}
	
	
	public <T> void addDefaultSetting (T value, String name, String description) {
		this.addDefaultSetting(value, name, description, true);
	}
	
	public <T> void addEmpty (Class<T> clazz, String name, String description) {
		this.addEmptySetting(clazz, name, description, true);
	}
	
	public <T> void addDefaultSetting (T value, String name, String description, boolean mandatory) {
		this.settingTypes.add(value.getClass());
		this.settingValues.add(value);
		this.settingNames.add(name);
		this.settingDescriptions.add(description);
		this.settingMandatory.add(mandatory);
	}
	
	public <T> void addEmptySetting (Class<T> clazz, String name, String description, boolean mandatory) {
		this.settingTypes.add(clazz);
		this.settingValues.add(null);
		this.settingNames.add(name);
		this.settingDescriptions.add(description);
		this.settingMandatory.add(mandatory);
	}
	
	public int getSize() {
		return settingTypes.size();
	}
	
	public Class<?> getTypeOf (int i) {
		return settingTypes.get(i);
	}
	
	public <T> T getValueOf (int i, Class<T> clazz) {
		return clazz.cast(settingValues.get(i));
	}
	
	public <T> void setValue (int i, T value) {
		if (value != null && !settingTypes.get(i).isAssignableFrom(value.getClass())) throw new IllegalStateException();
		settingValues.set(i, value);
	}
	
	public String getNameOf (int i) {
		return settingNames.get(i);
	}
	
	public String getDescriptionOf (int i) {
		return settingDescriptions.get(i);
	}
	
	public boolean checkConstraints() {
		for (int i = 0; i < getSize(); ++i) {
			if (settingMandatory.get(i) && settingValues.get(i) == null) {
				return false;
			} else if (settingValues.get(i) instanceof Configuration) {
				if (getValueOf(i, Configuration.class).checkConstraints() == false) {
					return false;
				}
			}
		}
		return true;
	}
	
	public int getIndexOf (String name) {
		return settingNames.indexOf(name);
	}
	
	public Collection<String> getKeys() {
		return Collections.unmodifiableList(this.settingNames);
	}

	public void clearStack() {
		this.stack.clear();
	}

	public <T> T get(String name, Class<T> clazz) {
		return getValueOf(getIndexOf(name), clazz);
	}
}
